<script setup>
import { ref, watch, computed, nextTick, getCurrentInstance } from 'vue'
import { hexToRgb2, rgbToHex2, hslToRgb2 } from './utils';

const props = defineProps({
  modelValue: {
    type: String,
    default: '#ff0000'
  },
  disabled: {
    type: Boolean,
    default: false
  },
  presetColors: {
    type: Array,
    default: () => ['#ff0000', '#00ff00', '#0000ff', '#ffff00', '#00ffff', '#ff00ff']
  }
})
const emit = defineEmits(['update:modelValue', 'change'])

const currentColor = ref(props.modelValue)
const hue = ref(0) // 0-360
const sat = ref(100) // 0-100
const val = ref(100) // 0-100
const instance = getCurrentInstance()

// 颜色转换
function hsvToRgb(h,s,v) {
  s /= 100; v /= 100;
  let c = v * s;
  let x = c * (1 - Math.abs((h / 60) % 2 - 1));
  let m = v - c;
  let r=0,g=0,b=0;
  if (h < 60) [r,g,b] = [c,x,0];
  else if (h < 120) [r,g,b] = [x,c,0];
  else if (h < 180) [r,g,b] = [0,c,x];
  else if (h < 240) [r,g,b] = [0,x,c];
  else if (h < 300) [r,g,b] = [x,0,c];
  else [r,g,b] = [c,0,x];
  return {
    r: Math.round((r+m)*255),
    g: Math.round((g+m)*255),
    b: Math.round((b+m)*255)
  }
}
function rgbToHex(r,g,b) {
  return '#' + [r,g,b].map(x=>x.toString(16).padStart(2,'0')).join('')
}
function hsvToHex(h,s,v) {
  const {r,g,b} = hsvToRgb(h,s,v)
  return rgbToHex(r,g,b)
}
function hexToRgb(hex) {
  let c = hex.replace('#','')
  if (c.length === 3) c = c.split('').map(x=>x+x).join('')
  const num = parseInt(c, 16)
  return {
    r: (num >> 16) & 255,
    g: (num >> 8) & 255,
    b: num & 255
  }
}
function rgbToHsv(r,g,b) {
  r /= 255; g /= 255; b /= 255;
  let max = Math.max(r,g,b), min = Math.min(r,g,b)
  let h=0,s=0,v=max
  let d = max-min
  s = max === 0 ? 0 : d/max
  if (max === min) h = 0
  else {
    switch(max){
      case r: h = (g-b)/d + (g<b?6:0); break;
      case g: h = (b-r)/d + 2; break;
      case b: h = (r-g)/d + 4; break;
    }
    h *= 60
  }
  return {h: Math.round(h), s: Math.round(s*100), v: Math.round(v*100)}
}
function hexToHsv(hex) {
  const {r,g,b} = hexToRgb(hex)
  return rgbToHsv(r,g,b)
}

function updateColor() {
    const rgb = hslToRgb2(hue.value/360, sat.value/100, val.value/100);
    const hex = rgbToHex2(rgb.r, rgb.g, rgb.b);
  currentColor.value = hex
  emit('update:modelValue', hex)
  emit('change', hex)
}
function handlePresetClick(preset) {
  if (props.disabled) return
  currentColor.value = preset
  const hsv = hexToHsv(preset)
  hue.value = hsv.h
  sat.value = hsv.s
  val.value = hsv.v
  emit('update:modelValue', preset)
  emit('change', preset)
}
// 外部v-model变化时同步hsv
watch(() => props.modelValue, (newColor) => {
  currentColor.value = newColor
  if (newColor) {
    const hsv = hexToHsv(newColor)
    hue.value = hsv.h
    sat.value = hsv.s
    val.value = hsv.v
  }
},{immediate:true})
const panelBg = computed(() => `hsl(${hue.value},100%,50%)`)

// 色板touch事件
function onPanelTouch(e) {
  if (props.disabled) return
  const touch = e.touches[0]
  nextTick(() => {
    uni.createSelectorQuery().in(instance.proxy).select('.panel-wrap').boundingClientRect(data => {
      if (!data) return
      // 兼容不同平台的坐标系
      const touchX = typeof touch.clientX !== 'undefined' ? touch.clientX : (typeof touch.x !== 'undefined' ? touch.x : touch.pageX)
      const touchY = typeof touch.clientY !== 'undefined' ? touch.clientY : (typeof touch.y !== 'undefined' ? touch.y : touch.pageY)
      const x = Math.max(0, Math.min(data.width, touchX - data.left))
      const y = Math.max(0, Math.min(data.height, touchY - data.top))
      let s = Math.round((x / data.width) * 100)
      let v = Math.round(100 - (y / data.height) * 100)
      sat.value = Math.max(0, Math.min(100, s))
      val.value = Math.max(0, Math.min(100, v))
      updateColor()
    }).exec()
  })
}
// 色条touch事件
function onBarTouch(e) {
  if (props.disabled) return
  const touch = e.touches[0]
  nextTick(() => {
    uni.createSelectorQuery().in(instance.proxy).select('.bar-bg').boundingClientRect(data => {
      if (!data) return
      const x = Math.max(0, Math.min(data.width, touch.pageX - data.left))
      let h = Math.round((x/data.width)*360)
      hue.value = Math.max(0, Math.min(360, h))
      updateColor()
    }).exec()
  })
}
</script>

<template>
  <view class="color-picker" :class="{disabled: props.disabled}">
    <!-- 当前颜色显示 -->
    <view class="color-display">
      <view class="current-color" :style="{ backgroundColor: currentColor }"></view>
    </view>
    <!-- 选色板 -->
    <view class="panel-wrap">
      <view class="panel-bg" :style="{background: panelBg}"
        @touchstart="onPanelTouch"
        @touchmove="onPanelTouch"
      >
        <view class="panel-white"></view>
        <view class="panel-black"></view>
        <view class="panel-cursor" :style="{
          left: `${sat.value}%`,
          top: `${100-val.value}%`,
          backgroundColor: currentColor.value
        }"></view>
      </view>
    </view>
    <!-- 色条 -->
    <view class="bar-wrap">
      <view class="bar-bg"
        @touchstart="onBarTouch"
        @touchmove="onBarTouch"
      >
        <view class="bar-cursor" :style="{left: `${hue.value/360*100}%`}" />
      </view>
    </view>
    <!-- 预设色 -->
    <view class="preset-row">
      <view v-for="preset in props.presetColors" :key="preset" class="preset-color" :style="{background:preset}" @click="()=>handlePresetClick(preset)" />
    </view>
  </view>
</template>

<style scoped>
.color-picker {
  width: 100%;
  padding: 20rpx 0;
  user-select: none;
}
.disabled {
  opacity: 0.5;
  pointer-events: none;
}
.color-display {
  margin-bottom: 16rpx;
}
.current-color {
  width: 100%;
  height: 40rpx;
  border-radius: 8rpx;
  border: 1px solid #ddd;
  margin-bottom: 8rpx;
}
.panel-wrap {
  width: 100%;
  height: 200rpx;
  position: relative;
  margin-bottom: 24rpx;
}
.panel-bg {
  width: 100%;
  height: 100%;
  border-radius: 12rpx;
  position: relative;
  overflow: hidden;
}
.panel-white {
  position: absolute;
  left: 0;top: 0;right: 0;bottom: 0;
  background: linear-gradient(to right,#fff,transparent);
  z-index: 1;
}
.panel-black {
  position: absolute;
  left: 0;top: 0;right: 0;bottom: 0;
  background: linear-gradient(to top,#000,transparent);
  z-index: 2;
}
.panel-cursor {
  position: absolute;
  width: 32rpx;
  height: 32rpx;
  border-radius: 50%;
  border: 2rpx solid #fff;
  box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.2);
  z-index: 4;
  transform: translate(-16rpx,-16rpx);
}
.bar-wrap {
  width: 100%;
  height: 32rpx;
  margin-bottom: 24rpx;
  position: relative;
}
.bar-bg {
  width: 100%;
  height: 100%;
  border-radius: 8rpx;
  background: linear-gradient(to right,#ff0000 0%,#ffff00 17%,#00ff00 33%,#00ffff 50%,#0000ff 67%,#ff00ff 83%,#ff0000 100%);
  position: relative;
}
.bar-cursor {
  position: absolute;
  top: 0;
  width: 8rpx;
  height: 100%;
  background: #fff;
  border-radius: 4rpx;
  box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.2);
  z-index: 3;
  transform: translateX(-4rpx);
}
.preset-row {
  display: flex;
  gap: 16rpx;
  margin-bottom: 16rpx;
}
.preset-color {
  width: 40rpx;
  height: 40rpx;
  border-radius: 50%;
  border: 2rpx solid #eee;
  cursor: pointer;
}
.preset-color:active {
  border-color: #409eff;
}
.color-value {
  text-align: center;
  font-size: 28rpx;
  color: #333;
  margin-top: 8rpx;
}
</style>
